package com.ocom.redis.aspect;


import com.ocom.redis.util.RedisUtil;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.CodeSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;


@Order(-2)
@Component
@Aspect
public class CleanRedisAspectImpl {

    /**
     * 声明泛型的时候自动注入 @Autowired 会失效，这里使用 @Resource 注解
     * 这种可以操作任意类型的数据，但是存入 redis 的数据为二进制，难以直接查看
     */
    @Resource
    private RedisUtil redisUtil;

    /**
     * 删除相关redis缓存
     * @param joinPoint      切点对象
     * @param cleanRedisAspect 注解内容
     */
    @AfterReturning(value = "@annotation(cleanRedisAspect)")
    public void process(JoinPoint joinPoint, CleanRedisAspect cleanRedisAspect) throws Throwable {
        Map<String, Object> param = this.mapParam(joinPoint);
        String key = this.buildKey(cleanRedisAspect, param);
        redisUtil.del(key);
    }

    /**
     * 获取参数签名 和 参数值，并封装为 MAP 集合
     * @param point 切点
     */
    private Map<String, Object> mapParam(JoinPoint point) {
        Map<String, Object> map = new HashMap<>();
        String[] names = ((CodeSignature) point.getSignature()).getParameterNames();
        Object[] values = point.getArgs();
        for (int i = 0; i < names.length; i++) {
            map.put(names[i], values[i]);
        }
        return map;
    }

    /**
     * 拼装 Redis 的健，具体规则：前缀 + :参数 + :参数 ......
     * 对于值为空，或者值为无效字符串的，忽略
     * 目前只支持 参数类型 为基础类型或字符串，如果有兴趣可以自行扩展该方法，比如通过反射机制实现支持类里面的元素
     */
    private String buildKey(CleanRedisAspect cacheRedis, Map<String, Object> param) {
        StringBuilder key = new StringBuilder(32);
        key.append(cacheRedis.prefix());
        key.append("#");
        for (int i = 0; i < cacheRedis.element().length; i++) {
            String name = cacheRedis.element()[i];
            Object value = param.get(name);
            if (value == null) {
                continue;
            }
            String str = value.toString();
            if (StringUtils.isBlank(str)) {
                continue;
            }
            key.append(name).append(":").append(str).append("#");
        }
        return key.toString();
    }
}